////////////////////////////////////////////////////////////////////////////////
// This file is part of the Mist compiler.                                    //
//                                                                            //
// Copyright (C) 2008, 2009 The Mist project                                  //
//                                                                            //
// For a list of contributors, see the COMMITTERS file.                       //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modify  //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// in the COPYING file. If not, see <http://www.gnu.org/licenses/>.           //
////////////////////////////////////////////////////////////////////////////////

///
/// \file
/// Contains the CodeStream class and related stream manipulators.
///

#ifndef CODESTREAM_HPP
#define CODESTREAM_HPP

////////////////////////////////////////////////////////////////////////////////
// Includes                                                                   //
////////////////////////////////////////////////////////////////////////////////

#include <ostream>
using std::ostream;
using std::endl;

#include <string>
using std::string;

#include "common/types.hpp"

////////////////////////////////////////////////////////////////////////////////
// Forward Declarations                                                       //
////////////////////////////////////////////////////////////////////////////////

class CodeStream;

////////////////////////////////////////////////////////////////////////////////
// Stream Manipulators                                                        //
////////////////////////////////////////////////////////////////////////////////

/// Add one level to the indentation depth of \a stream.
/// Use this as a manipulator with \c stream.operator<<.
///
/// \return \a stream
///
CodeStream& indent(CodeStream& stream);

/// Reduce the indentation depth of \a stream by one level.
/// Use this as a manipulator with \a stream.operator<<.
///
/// \return \a stream
///
/// \pre stream.indent_size() > 0
///
CodeStream& unindent(CodeStream& stream);

////////////////////////////////////////////////////////////////////////////////
// CodeStream Class                                                           //
////////////////////////////////////////////////////////////////////////////////

/// An output stream class that takes any other output stream class and add
/// some extra functionality useful for printing code, like automatic
/// indentation. For the most part, it works just like ostream.
///
class CodeStream : public ostream {
	public:
		
		/// Construct a code stream.
		///
		CodeStream(
			ostream& stream,      ///< the stream to send the plain output to
			ushort indentSize = 4 ///< the tab-size for indentation
		);
		
		/// \defgroup StreamingOperators The streaming operators,
		///                              automatically handling indentation.
		///
		// @{
		template <class T>
		CodeStream& operator<<(T data);
		CodeStream& operator<<(const char* data);
		CodeStream& operator<<(string data);
		CodeStream& operator<<(char c);
		CodeStream& put(char c);
		CodeStream& operator<< (ostream& ( *pf )(ostream&));
		CodeStream& operator<< (CodeStream& ( *pf )(CodeStream&));
		// @}
		
		/// Get the current indentation of the code.
		///
		ushort indent_size() const;
		
		friend CodeStream& indent(CodeStream& stream);
		friend CodeStream& unindent(CodeStream& stream);
	
	private: // functions
		
		void maybe_indent();
	
	private: // variables
		
		ostream&     _stream;
		bool         _newline;
		uint         _indentation;
		const ushort _indentSize;
};

////////////////////////////////////////////////////////////////////////////////
// Template Function Implementations                                          //
////////////////////////////////////////////////////////////////////////////////

template <class T>
CodeStream& CodeStream::operator<<(T data) {
	maybe_indent();
	_stream << data;
	return *this;
}

#endif // CODESTREAM_HPP
